#include "../build/factoryFinder/soapH.h"
#include "../build/factoryFinder/FactoryFinder.nsmap"
#include "libservice.h"

char *serviceName = "FactoryFinder";
char * uuid;

char * get_service(struct soap *soap, char * addressWsdl) {
	// return physical address of a service wsdl
	xmlDoc *doc = NULL;
	xmlNode *root = NULL;
	xmlNode *service = NULL;

	char * service_file;
	config_get_string(&service_file, "SERVICES_FILE");

	doc = xmlReadFile(service_file, NULL, XML_PARSE_NOWARNING);

	if (!doc) {
		fprintf(stderr, "factoryFinder.c: deployment file %s doesn't exist",
				service_file);
		return NULL;
	}

	root = xmlDocGetRootElement(doc);
	service = root ->children;

	while (service) {
		if (strstr(xmlGetProp(service, "addressWsdl"), addressWsdl)) {
			return xmlGetProp(service, "path");
		}
		service = service->next;
	}

	xmlFreeDoc(doc);

	return NULL;

}

int http_get(struct soap *soap) {
	// implements HTTP GET method
	char *s = strchr(soap->path, '?');
	if (!s || strcmp(s, "?wsdl"))
		return SOAP_GET_METHOD;
	FILE * f = fopen(get_service(soap, soap->path), "rb");
	if (!f)
		return 404;
	soap->http_content = "text/xml";
	soap_response(soap, SOAP_FILE);
	for (;;) {
		int r = fread(soap->tmpbuf, 1, sizeof(soap->tmpbuf), f);
		if (!r)
			break;
		if (soap_send_raw(soap, soap->tmpbuf, r))
			break;
	}
	fclose(f);
	soap_end_send(soap);
	return SOAP_OK;
}

void *process_request(void *soap) {
	pthread_detach(pthread_self());
	soap_serve((struct soap*) soap);
	soap_destroy((struct soap*) soap);
	// dealloc C++ data
	soap_end((struct soap*) soap);
	// dealloc data and clean up
	soap_done((struct soap*) soap);
	// detach soap struct
	free(soap);
	return NULL;
}

int main(int argc, char **argv) {

	struct soap soap;
	soap_init(&soap);
	soap.fget = http_get;

	soap.send_timeout = 5;
	soap.recv_timeout = 5;
	soap.accept_timeout = 3600;
	// server stops after 1 hour of inactivity
	soap.max_keep_alive = 100;
	// max keep-alive sequence

	void *process_request(void*);
	struct soap *tsoap;
	pthread_t tid;
	SOAP_SOCKET m, s;

	int factory_finder_port;
	config_get_int(&factory_finder_port, "FACTORY_FINDER_PORT");
	char * factory_finder_ip;
	config_get_string(&factory_finder_ip, "FACTORY_FINDER_IP");

	m = soap_bind(&soap, NULL, factory_finder_port, 100);

	if (!soap_valid_socket(m)) {
		fprintf(stderr,
				"factoryFinder.c: %s failed to start, port %d is busy\n",
				serviceName, factory_finder_port);
		exit(1);
	}

	service_set_uuid(&uuid);
	char *serviceWsdl = malloc(256);
	sprintf(serviceWsdl, "http://%s:%d/FactoryFinder?wsdl", factory_finder_ip,
			factory_finder_port);
	char * awsm_home;
	config_get_string(&awsm_home, "AWSM_HOME");
	service_deploy(serviceName, serviceWsdl, strcat(awsm_home,
			"/build/factoryFinder/FactoryFinder.wsdl"), uuid);
	fprintf(stderr, "factoryFinder.c: %s with id %s started\n", serviceName,
			uuid);

	for (;;) {
		s = soap_accept(&soap);
		if (!soap_valid_socket(s)) {
			if (soap.errnum) {
				soap_print_fault(&soap, stderr);
				exit(1);
			}
			fprintf(stderr, "factoryFinder.c: server timed out\n");
			service_undeploy(serviceName, uuid);
			break;
		}
		fprintf(
				stderr,
				"[soap] factoryFinder.c: request from IP %d.%d.%d.%d processed\n",
				(soap.ip >> 24) & 0xFF, (soap.ip >> 16) & 0xFF, (soap.ip >> 8)
						& 0xFF, soap.ip & 0xFF);
		tsoap = soap_copy(&soap);
		// make a safe copy
		if (!tsoap)
			break;
		pthread_create(&tid, NULL, (void*(*)(void*)) process_request,
				(void*) tsoap);
	}

	soap_done(&soap);
	// detach soap struct
	return 0;

}

int ns__register(struct soap *soap, char * criteriaXml, char * addressWsdl,
		char ** message) {
	xmlDoc *doc = NULL;
	xmlNode *root = NULL;
	xmlNode *service = NULL;
	xmlNode *serviceNew = NULL;

	xmlDoc *criteriaDoc = NULL;
	xmlNode *criteriaRoot = NULL;
	xmlNode *criteria = NULL;

	char * tmp = soap_malloc(soap, 1024);
	sprintf(tmp, "<criteria>%s</criteria>", criteriaXml);

	criteriaXml = tmp;

	criteriaDoc = xmlParseDoc(criteriaXml);
	criteriaRoot = xmlDocGetRootElement(criteriaDoc);
	criteria = criteriaRoot->children;

	char * registration_file;
	config_get_string(&registration_file, "REGISTRATION_FILE");

	pthread_mutex_t cs_mutex = PTHREAD_MUTEX_INITIALIZER;
	pthread_mutex_lock(&cs_mutex);
	//enter critical section

	doc = xmlReadFile(registration_file, NULL, XML_PARSE_NOWARNING);

	if (!doc) {

		doc = xmlNewDoc("1.0");
		root = xmlNewNode(NULL, "registrations");
		xmlDocSetRootElement(doc, root);

	}

	root = xmlDocGetRootElement(doc);

	service = root ->children;
	*message = soap_malloc(soap, 1024);

	while (service) {
		//check if the give wsdlAddress was already registered
		if (xmlStrEqual(xmlGetProp(service, "wsdlAddress"), addressWsdl)) {
			sprintf(*message, "factoryFinder.c: %s already registered",
					addressWsdl);
			break;
		}
		service = service->next;
	}

	if (!service) {

		serviceNew = xmlNewNode(NULL, "registration");
		xmlNewProp(serviceNew, "wsdlAddress", addressWsdl);
		while (criteria) {
			xmlNode *node = xmlNewChild(serviceNew, NULL, criteria->name,
					criteria->content);
			xmlNewProp(node, "value", xmlGetProp(criteria, "value"));
			criteria = criteria->next;
		}
		sprintf(*message, "factoryFinder.c: %s registered", addressWsdl);
	}

	xmlAddChild(root, serviceNew);

	xmlSaveFile(registration_file, doc);

	pthread_mutex_unlock(&cs_mutex);
	pthread_mutex_destroy(&cs_mutex);
	//leave critical section

	xmlFreeDoc(doc);
	xmlFreeDoc(criteriaDoc);

	return SOAP_OK;

}

int ns__unregister(struct soap *soap, char * addressWsdl, char ** message) {
	xmlDoc *doc = NULL;
	xmlDoc *docNew = NULL;

	xmlNode *root = NULL;
	xmlNode *rootNew = NULL;

	xmlNode *factory = NULL;

	int registered = 0;

	char * registration_file;
	config_get_string(&registration_file, "REGISTRATION_FILE");

	pthread_mutex_t cs_mutex = PTHREAD_MUTEX_INITIALIZER;
	pthread_mutex_lock(&cs_mutex);
	// enter critical section

	doc = xmlReadFile(registration_file, NULL, XML_PARSE_NOWARNING);
	*message = soap_malloc(soap, 1024);

	if (!doc) {
		char *error = soap_malloc(soap, 1024);

		sprintf(error, "factoryFinder.c: registrations file %s doesn't exist",
				registration_file);
		return soap_sender_fault(soap, error, NULL);
	}

	root = xmlDocGetRootElement(doc);
	factory = root ->children;

	docNew = xmlNewDoc("1.0");
	rootNew = xmlNewNode(NULL, "registrations");
	xmlDocSetRootElement(docNew, rootNew);

	while (factory) {
		//traverse factory list, add factories to the new list
		//except the one which should be removed

		if (!xmlStrEqual(xmlGetProp(factory, "wsdlAddress"), addressWsdl)) {
			xmlAddChild(rootNew, xmlCopyNode(factory, 1));

		} else {
			sprintf(*message, "factoryFinder.c: %s unregistered", addressWsdl);
			registered = 1;
		}
		factory = factory->next;
	}
	if (!registered) {
		sprintf(*message, "factoryFinder.c: %s not registered\n", addressWsdl);
	}

	xmlSaveFile(registration_file, docNew);

	pthread_mutex_unlock(&cs_mutex);
	pthread_mutex_destroy(&cs_mutex);
	//leave critical section

	xmlFreeDoc(doc);
	xmlFreeDoc(docNew);

	return SOAP_OK;

}

int ns__findFactories(struct soap *soap, char * criteriaXml,
		struct ArrayOfString ** factoriesWsdl) {
	xmlDoc *doc = NULL;
	xmlDoc *criteriaDoc = NULL;
	xmlNode *root = NULL;
	xmlNode *service = NULL;
	xmlNode *criteria = NULL;

	char *wsdlAddress = NULL;
	char *serviceName = NULL;

	char * tmp = soap_malloc(soap, 1024);
	sprintf(tmp, "<criteria>%s</criteria>", criteriaXml);

	criteriaXml = tmp;

	criteriaDoc = xmlParseDoc(criteriaXml);
	criteria = xmlDocGetRootElement(criteriaDoc)->children;

	while (criteria) {
		if (xmlStrEqual(criteria ->name, "interface")) {
			serviceName = xmlGetProp(criteria, "value");
			break;
		}
		criteria = criteria->next;
	}

	char * registration_file;
	config_get_string(&registration_file, "REGISTRATION_FILE");

	pthread_mutex_t cs_mutex = PTHREAD_MUTEX_INITIALIZER;
	pthread_mutex_lock(&cs_mutex);
	//enter critical section

	doc = xmlReadFile(registration_file, NULL, XML_PARSE_NOWARNING);

	if (!doc) {
		char *s = soap_malloc(soap, 1024);
		sprintf(s, "registrations file %s doesn't exist", registration_file);
		return soap_sender_fault(soap, s, NULL);
	}

	root = xmlDocGetRootElement(doc);

	// first loop only to find out the number of factory
	int numberOfFactory = 0;
	service = root ->children;
	while (service) {
		wsdlAddress = xmlGetProp(service, "wsdlAddress");
		criteria = service->children;
		while (criteria) {
			if (xmlStrEqual(xmlGetProp(criteria, "value"), serviceName)) {
				numberOfFactory++;

			}
			criteria = criteria ->next;
		}
		service = service->next;
	}

	*factoriesWsdl = soap_malloc(soap, sizeof(struct ArrayOfString *));
	(*factoriesWsdl)->__size = numberOfFactory;
	(*factoriesWsdl)->__ptr = soap_malloc(soap, (*factoriesWsdl)->__size
			* sizeof(char *));
	int index = 0;
	service = root ->children;
	while (service) {
		wsdlAddress = xmlGetProp(service, "wsdlAddress");
		criteria = service->children;
		while (criteria) {
			if (xmlStrEqual(xmlGetProp(criteria, "value"), serviceName)) {
				(*factoriesWsdl)->__ptr[index] = soap_malloc(soap, 1024);
				strcpy((*factoriesWsdl)->__ptr[index], wsdlAddress);
				index++;
			}
			criteria = criteria ->next;
		}
		service = service->next;
	}

	pthread_mutex_unlock(&cs_mutex);
	pthread_mutex_destroy(&cs_mutex);
	//leave critical section

	xmlFreeNode(service);
	xmlFreeDoc(doc);

	return SOAP_OK;
}
